Micron Document
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| SparkN0de-git | SparkN0de |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Node / ReticulumProjects / MeshChatX.git / files / scripts / build-android-wheels-local.sh

Displaying Raw • Download


scripts/build-android-wheels-local.sh e41daa5b65e07a5baa1aaad32d97f90482f1cd1c (e41daa5b) Text, 42.93 KB

T8b949e#!/usr/bin/env bash
Tffa657set -euo pipefail

usageTff7b72(Tff7b72) Tff7b72{
cat Ta5d6ff<<'EOF'
Build Android wheels locally for Chaquopy (Linux x86_64 host).

This script:
1) Clones/updates Chaquopy sources locally
2) Downloads a matching Chaquopy Python target toolchain
3) Builds pycodec2 Android wheels with Chaquopy's build-wheel tool
4) Optionally patches LXST wheel metadata for local Android constraints
5) Vendors the bleak pure-python wheel from PyPI
6) Vendors httpx[http2] and its pure-python dependency wheels from PyPI
(RNS-over-HTTP / HTTPInterface client support)
7) Patches the rns wheel so its Android RNodeInterface never calls
RNS.panic() (os._exit) when usbserial4a/jnius are missing
8) Builds every recipe under android/chaquopy-recipes/ for each requested
ABI (currently: cryptography, miniaudio, aiohttp, cbor2, ...)
9) Copies outputs to android/vendor

Usage:
scripts/build-android-wheels-local.sh [options]

Options:
--python-minor X.Y Python minor for target wheels (default: 3.11)
--target-version V Explicit Chaquopy target version (default: auto latest for python minor)
--chaquopy-ref REF Chaquopy git ref/commit to checkout (default: master)
--abis LIST Comma-separated ABIs (default: arm64-v8a,x86_64,armeabi-v7a)
--api-level N Android API level for wheel tag (default: 24)
--pycodec2-version V pycodec2 version to build (default: 4.1.1)
--numpy-version V NumPy version used during pycodec2 build (default: 1.26.2)
--lxst-version V LXST wheel version for metadata patch (default: 0.5.1)
--bleak-version V bleak pure-python wheel version to vendor (default: 3.0.2)
--httpx-version V httpx pure-python wheel version to vendor (default: 0.28.1)
--rns-version V rns wheel version to patch (default: 1.4.2)
--no-lxst-patch Skip LXST metadata patch
--no-rns-patch Skip RNS Android RNodeInterface patch
--only-recipes LIST Comma-separated recipe directory names under
android/chaquopy-recipes to build. When set, the
NumPy, pycodec2/chaquopy-libcodec2 and LXST steps
are skipped and only matching custom recipes run.
--work-dir PATH Working directory (default: ./.local/chaquopy-build-wheel)
--out-dir PATH Output wheel directory (default: ./android/vendor)
-h, --help Show this help
EOF
Tff7b72}

Te6edf3ROOT_DIRTff7b72=Ta5d6ff"Tff7b72$(Tffa657cd Ta5d6ff"Tff7b72$(dirname Ta5d6ff"Tffd700${Te6edf3BASH_SOURCETb4b4b4[0]Tffd700}Ta5d6ff"Tff7b72)Ta5d6ff/..Ta5d6ff" Tff7b72&& Tffa657pwdTff7b72)Ta5d6ff"

Tff7b72if Tff7b72[ Ta5d6ff"Tffd700${Te6edf3MESHCHATX_OFFLINE_BUILDTff7b72:-Tffd700}Ta5d6ff" Tff7b72= Ta5d6ff"1" Tff7b72]Tb4b4b4; Tff7b72then
Tffa657echo Ta5d6ff"MESHCHATX_OFFLINE_BUILD=1: Android wheel build requires network access. Pre-build wheels offline or unset MESHCHATX_OFFLINE_BUILD." >Tb4b4b4&T79c0ff2
Tffa657exit T79c0ff1
Tff7b72fi

Te6edf3PYTHON_MINORTff7b72=Ta5d6ff"3.11"
Te6edf3TARGET_VERSIONTff7b72=Ta5d6ff""
Te6edf3CHAQUOPY_REFTff7b72=Ta5d6ff"Tffd700${Te6edf3CHAQUOPY_REFTff7b72:-Te6edf3masterTffd700}Ta5d6ff"
Te6edf3ABI_LISTTff7b72=Ta5d6ff"arm64-v8a,x86_64,armeabi-v7a"
Te6edf3API_LEVELTff7b72=Ta5d6ff"24"
Te6edf3PYCODEC2_VERSIONTff7b72=Ta5d6ff"4.1.1"
Te6edf3LIBCODEC2_VERSIONTff7b72=Ta5d6ff"1.2.0"
Te6edf3NUMPY_VERSIONTff7b72=Ta5d6ff"1.26.2"
Te6edf3LXST_VERSIONTff7b72=Ta5d6ff"0.5.1"
Te6edf3BLEAK_VERSIONTff7b72=Ta5d6ff"3.0.2"
Te6edf3HTTPX_VERSIONTff7b72=Ta5d6ff"0.28.1"
Te6edf3RNS_VERSIONTff7b72=Ta5d6ff"1.4.2"
Te6edf3PATCH_LXSTTff7b72=Ta5d6ff"1"
Te6edf3PATCH_RNSTff7b72=Ta5d6ff"1"
Te6edf3ONLY_RECIPESTff7b72=Ta5d6ff""
Te6edf3WORK_DIRTff7b72=Ta5d6ff"Tffd700${Te6edf3ROOT_DIRTffd700}Ta5d6ff/.local/chaquopy-build-wheelTa5d6ff"
Te6edf3OUT_DIRTff7b72=Ta5d6ff"Tffd700${Te6edf3ROOT_DIRTffd700}Ta5d6ff/android/vendorTa5d6ff"

Tff7b72while Tff7b72[Tff7b72[ Te6edf3$# -gt T79c0ff0 Tff7b72]Tff7b72]Tb4b4b4; Tff7b72do
Tff7b72case Ta5d6ff"Te6edf3$1Ta5d6ff" Tff7b72in
--python-minorTff7b72)
Te6edf3PYTHON_MINORTff7b72=Ta5d6ff"Tffd700${Te6edf32Tb4b4b4:Tb4b4b4?missing value for --python-minorTffd700}Ta5d6ff"
Tffa657shift T79c0ff2
Tb4b4b4;Tb4b4b4;
--target-versionTff7b72)
Te6edf3TARGET_VERSIONTff7b72=Ta5d6ff"Tffd700${Te6edf32Tb4b4b4:Tb4b4b4?missing value for --target-versionTffd700}Ta5d6ff"
Tffa657shift T79c0ff2
Tb4b4b4;Tb4b4b4;
--chaquopy-refTff7b72)
Te6edf3CHAQUOPY_REFTff7b72=Ta5d6ff"Tffd700${Te6edf32Tb4b4b4:Tb4b4b4?missing value for --chaquopy-refTffd700}Ta5d6ff"
Tffa657shift T79c0ff2
Tb4b4b4;Tb4b4b4;
--abisTff7b72)
Te6edf3ABI_LISTTff7b72=Ta5d6ff"Tffd700${Te6edf32Tb4b4b4:Tb4b4b4?missing value for --abisTffd700}Ta5d6ff"
Tffa657shift T79c0ff2
Tb4b4b4;Tb4b4b4;
--api-levelTff7b72)
Te6edf3API_LEVELTff7b72=Ta5d6ff"Tffd700${Te6edf32Tb4b4b4:Tb4b4b4?missing value for --api-levelTffd700}Ta5d6ff"
Tffa657shift T79c0ff2
Tb4b4b4;Tb4b4b4;
--pycodec2-versionTff7b72)
Te6edf3PYCODEC2_VERSIONTff7b72=Ta5d6ff"Tffd700${Te6edf32Tb4b4b4:Tb4b4b4?missing value for --pycodec2-versionTffd700}Ta5d6ff"
Tffa657shift T79c0ff2
Tb4b4b4;Tb4b4b4;
--numpy-versionTff7b72)
Te6edf3NUMPY_VERSIONTff7b72=Ta5d6ff"Tffd700${Te6edf32Tb4b4b4:Tb4b4b4?missing value for --numpy-versionTffd700}Ta5d6ff"
Tffa657shift T79c0ff2
Tb4b4b4;Tb4b4b4;
--lxst-versionTff7b72)
Te6edf3LXST_VERSIONTff7b72=Ta5d6ff"Tffd700${Te6edf32Tb4b4b4:Tb4b4b4?missing value for --lxst-versionTffd700}Ta5d6ff"
Tffa657shift T79c0ff2
Tb4b4b4;Tb4b4b4;
--bleak-versionTff7b72)
Te6edf3BLEAK_VERSIONTff7b72=Ta5d6ff"Tffd700${Te6edf32Tb4b4b4:Tb4b4b4?missing value for --bleak-versionTffd700}Ta5d6ff"
Tffa657shift T79c0ff2
Tb4b4b4;Tb4b4b4;
--httpx-versionTff7b72)
Te6edf3HTTPX_VERSIONTff7b72=Ta5d6ff"Tffd700${Te6edf32Tb4b4b4:Tb4b4b4?missing value for --httpx-versionTffd700}Ta5d6ff"
Tffa657shift T79c0ff2
Tb4b4b4;Tb4b4b4;
--rns-versionTff7b72)
Te6edf3RNS_VERSIONTff7b72=Ta5d6ff"Tffd700${Te6edf32Tb4b4b4:Tb4b4b4?missing value for --rns-versionTffd700}Ta5d6ff"
Tffa657shift T79c0ff2
Tb4b4b4;Tb4b4b4;
--no-lxst-patchTff7b72)
Te6edf3PATCH_LXSTTff7b72=Ta5d6ff"0"
Tffa657shift
Tb4b4b4;Tb4b4b4;
--no-rns-patchTff7b72)
Te6edf3PATCH_RNSTff7b72=Ta5d6ff"0"
Tffa657shift
Tb4b4b4;Tb4b4b4;
--only-recipesTff7b72)
Te6edf3ONLY_RECIPESTff7b72=Ta5d6ff"Tffd700${Te6edf32Tb4b4b4:Tb4b4b4?missing value for --only-recipesTffd700}Ta5d6ff"
Tffa657shift T79c0ff2
Tb4b4b4;Tb4b4b4;
--work-dirTff7b72)
Te6edf3WORK_DIRTff7b72=Ta5d6ff"Tffd700${Te6edf32Tb4b4b4:Tb4b4b4?missing value for --work-dirTffd700}Ta5d6ff"
Tffa657shift T79c0ff2
Tb4b4b4;Tb4b4b4;
--out-dirTff7b72)
Te6edf3OUT_DIRTff7b72=Ta5d6ff"Tffd700${Te6edf32Tb4b4b4:Tb4b4b4?missing value for --out-dirTffd700}Ta5d6ff"
Tffa657shift T79c0ff2
Tb4b4b4;Tb4b4b4;
-hTb4b4b4|--helpTff7b72)
usage
Tffa657exit T79c0ff0
Tb4b4b4;Tb4b4b4;
*Tff7b72)
Tffa657echo Ta5d6ff"Ta5d6ffUnknown argument: Te6edf3$1Ta5d6ff" >Tb4b4b4&T79c0ff2
usage
Tffa657exit T79c0ff1
Tb4b4b4;Tb4b4b4;
Tff7b72esac
Tff7b72done

require_cmdTff7b72(Tff7b72) Tff7b72{
Tff7b72if ! Tffa657command -v Ta5d6ff"Te6edf3$1Ta5d6ff" >/dev/null T79c0ff2>Tb4b4b4&T79c0ff1Tb4b4b4; Tff7b72then
Tffa657echo Ta5d6ff"Ta5d6ffMissing required command: Te6edf3$1Ta5d6ff" >Tb4b4b4&T79c0ff2
Tffa657exit T79c0ff1
Tff7b72fi
Tff7b72}

abi_to_platform_tagTff7b72(Tff7b72) Tff7b72{
Tff7b72case Ta5d6ff"Te6edf3$1Ta5d6ff" Tff7b72in
arm64-v8aTff7b72) Tffa657echo Ta5d6ff"android_21_arm64_v8a" Tb4b4b4;Tb4b4b4;
x86_64Tff7b72) Tffa657echo Ta5d6ff"android_21_x86_64" Tb4b4b4;Tb4b4b4;
armeabi-v7aTff7b72) Tffa657echo Ta5d6ff"android_16_armeabi_v7a" Tb4b4b4;Tb4b4b4;
x86Tff7b72) Tffa657echo Ta5d6ff"android_16_x86" Tb4b4b4;Tb4b4b4;
*Tff7b72)
Tffa657echo Ta5d6ff"Ta5d6ffUnsupported ABI: Te6edf3$1Ta5d6ff" >Tb4b4b4&T79c0ff2
Tffa657exit T79c0ff1
Tb4b4b4;Tb4b4b4;
Tff7b72esac
Tff7b72}

discover_latest_targetTff7b72(Tff7b72) Tff7b72{
Tffa657local Te6edf3python_minorTff7b72=Ta5d6ff"Te6edf3$1Ta5d6ff"
Tffa657local metadata versions latest
Te6edf3metadataTff7b72=Ta5d6ff"Tff7b72$(curl -fsSL Ta5d6ff"https://repo.maven.apache.org/maven2/com/chaquo/python/target/maven-metadata.xml"Tff7b72)Ta5d6ff"
Te6edf3versionsTff7b72=Ta5d6ff"Tff7b72$(Tffa657printf Ta5d6ff'%s\n' Ta5d6ff"Te6edf3$metadataTa5d6ff" Tffea00\
Tb4b4b4| sed -n Ta5d6ff's|.*<version>\(.*\)</version>.*|\1|p' Tffea00\
Tb4b4b4| awk -v Te6edf3pTff7b72=Ta5d6ff"Tffd700${Te6edf3python_minorTffd700}Ta5d6ff.Ta5d6ff" Ta5d6ff'index($0, p)==1'Tff7b72)Ta5d6ff"
Te6edf3latestTff7b72=Ta5d6ff"Tff7b72$(Tffa657printf Ta5d6ff'%s\n' Ta5d6ff"Te6edf3$versionsTa5d6ff" Tb4b4b4| sort -V Tb4b4b4| tail -n T79c0ff1Tff7b72)Ta5d6ff"
Tff7b72if Tff7b72[Tff7b72[ -z Ta5d6ff"Tffd700${Te6edf3latestTffd700}Ta5d6ff" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
Tffa657echo Ta5d6ff"Ta5d6ffCould not discover Chaquopy target version for Python Tffd700${Te6edf3python_minorTffd700}Ta5d6ff" >Tb4b4b4&T79c0ff2
Tffa657exit T79c0ff1
Tff7b72fi
Tffa657printf Ta5d6ff'%s\n' Ta5d6ff"Te6edf3$latestTa5d6ff"
Tff7b72}

require_cmd git
require_cmd curl
require_cmd sed
require_cmd awk
require_cmd sort
require_cmd patchelf

Tff7b72case Ta5d6ff"Ta5d6ff,Tffd700${Te6edf3ABI_LISTTffd700}Ta5d6ff,Ta5d6ff" Tff7b72in
*,armeabi-v7a,*Tff7b72)
Tff7b72if Tff7b72[Tff7b72[ -z Ta5d6ff"Tffd700${Te6edf3ONLY_RECIPESTffd700}Ta5d6ff" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
Tff7b72if Tff7b72[Tff7b72[ -z Ta5d6ff"Tffd700${Te6edf3ANDROID_HOMETff7b72:-Tffd700}Ta5d6ff" Tff7b72&& -z Ta5d6ff"Tffd700${Te6edf3ANDROID_SDK_ROOTTff7b72:-Tffd700}Ta5d6ff" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
Tffa657echo Ta5d6ff"armeabi-v7a: NumPy is built from source and needs the Android SDK/NDK (Chaquopy android-env.sh)." >Tb4b4b4&T79c0ff2
Tffa657echo Ta5d6ff"Set ANDROID_HOME or ANDROID_SDK_ROOT to your SDK root (with cmdline-tools/latest/bin/sdkmanager)." >Tb4b4b4&T79c0ff2
Tffa657exit T79c0ff1
Tff7b72fi
Tffa657export Te6edf3ANDROID_HOMETff7b72=Ta5d6ff"Tffd700${Te6edf3ANDROID_HOMETff7b72:-Tffd700${Te6edf3ANDROID_SDK_ROOTTffd700}Tffd700}Ta5d6ff"
Tffa657export Te6edf3ANDROID_SDK_ROOTTff7b72=Ta5d6ff"Tffd700${Te6edf3ANDROID_SDK_ROOTTff7b72:-Tffd700${Te6edf3ANDROID_HOMETffd700}Tffd700}Ta5d6ff"
Tff7b72if Tff7b72[Tff7b72[ ! -x Ta5d6ff"Tffd700${Te6edf3ANDROID_HOMETffd700}Ta5d6ff/cmdline-tools/latest/bin/sdkmanagerTa5d6ff" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
Tffa657echo Ta5d6ff"Ta5d6ffarmeabi-v7a: expected sdkmanager at Tffd700${Te6edf3ANDROID_HOMETffd700}Ta5d6ff/cmdline-tools/latest/bin/sdkmanagerTa5d6ff" >Tb4b4b4&T79c0ff2
Tffa657exit T79c0ff1
Tff7b72fi
Tff7b72else
Tffa657export Te6edf3ANDROID_HOMETff7b72=Ta5d6ff"Tffd700${Te6edf3ANDROID_HOMETff7b72:-Tffd700${Te6edf3ANDROID_SDK_ROOTTff7b72:-Tffd700}Tffd700}Ta5d6ff"
Tffa657export Te6edf3ANDROID_SDK_ROOTTff7b72=Ta5d6ff"Tffd700${Te6edf3ANDROID_SDK_ROOTTff7b72:-Tffd700${Te6edf3ANDROID_HOMETff7b72:-Tffd700}Tffd700}Ta5d6ff"
Tff7b72fi
Tb4b4b4;Tb4b4b4;
Tff7b72esac

Te6edf3PYTHON_BINTff7b72=Ta5d6ff"Tffd700${Te6edf3PYTHON_BINTff7b72:-Te6edf3pythonTffd700${Te6edf3PYTHON_MINORTffd700}Tffd700}Ta5d6ff"
Tff7b72if ! Tffa657command -v Ta5d6ff"Tffd700${Te6edf3PYTHON_BINTffd700}Ta5d6ff" >/dev/null T79c0ff2>Tb4b4b4&T79c0ff1Tb4b4b4; Tff7b72then
Tffa657echo Ta5d6ff"Ta5d6ffRequired interpreter not found: Tffd700${Te6edf3PYTHON_BINTffd700}Ta5d6ff" >Tb4b4b4&T79c0ff2
Tffa657echo Ta5d6ff"Ta5d6ffInstall Python Tffd700${Te6edf3PYTHON_MINORTffd700}Ta5d6ff (e.g. uv python install Tffd700${Te6edf3PYTHON_MINORTffd700}Ta5d6ff) or set PYTHON_BIN.Ta5d6ff" >Tb4b4b4&T79c0ff2
Tffa657exit T79c0ff1
Tff7b72fi

mkdir -p Ta5d6ff"Tffd700${Te6edf3WORK_DIRTffd700}Ta5d6ff" Ta5d6ff"Tffd700${Te6edf3OUT_DIRTffd700}Ta5d6ff"

Te6edf3CHAQUOPY_DIRTff7b72=Ta5d6ff"Tffd700${Te6edf3WORK_DIRTffd700}Ta5d6ff/chaquopyTa5d6ff"
Tff7b72if Tff7b72[Tff7b72[ ! -d Ta5d6ff"Tffd700${Te6edf3CHAQUOPY_DIRTffd700}Ta5d6ff/.gitTa5d6ff" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
git clone --depth T79c0ff1 https://github.com/chaquo/chaquopy.git Ta5d6ff"Tffd700${Te6edf3CHAQUOPY_DIRTffd700}Ta5d6ff"
Tff7b72fi
git -C Ta5d6ff"Tffd700${Te6edf3CHAQUOPY_DIRTffd700}Ta5d6ff" fetch --depth T79c0ff1 origin Ta5d6ff"Tffd700${Te6edf3CHAQUOPY_REFTffd700}Ta5d6ff"
git -C Ta5d6ff"Tffd700${Te6edf3CHAQUOPY_DIRTffd700}Ta5d6ff" checkout --detach FETCH_HEAD

Tff7b72if Tff7b72[Tff7b72[ -z Ta5d6ff"Tffd700${Te6edf3TARGET_VERSIONTffd700}Ta5d6ff" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
Te6edf3TARGET_VERSIONTff7b72=Ta5d6ff"Tff7b72$(discover_latest_target Ta5d6ff"Tffd700${Te6edf3PYTHON_MINORTffd700}Ta5d6ff"Tff7b72)Ta5d6ff"
Tff7b72fi
Tffa657echo Ta5d6ff"Ta5d6ffUsing Chaquopy git ref: Tffd700${Te6edf3CHAQUOPY_REFTffd700}Ta5d6ff"
Tffa657echo Ta5d6ff"Ta5d6ffUsing Chaquopy target version: Tffd700${Te6edf3TARGET_VERSIONTffd700}Ta5d6ff"

apply_chaquopy_libpython3_link_fixTff7b72(Tff7b72) Tff7b72{
Ta5d6ff"Tffd700${Te6edf3PYTHON_BINTffd700}Ta5d6ff" - Ta5d6ff<<'PY'
from pathlib import Path
import os
import sys

path = Path(os.environ["BUILD_WHEEL_PYTa5d6ff"])
text = path.read_text()
changed = False

if "chaquopy_python_abi3_linkTa5d6ff" not in text:
needle = ' run(f"ln -s Tff7b72{filenameTff7b72} Tff7b72{reqs_lib_dirTff7b72}/Tff7b72{link_filenameTff7b72}Ta5d6ff")'
if needle not in text:
print("Could not find SONAME symlink loop Tff7b72in build-wheel.pyTa5d6ff", file=sys.stderr)
sys.exit(1)
insert = needle + "Ta5d6ff""

T8b949e# PyO3 0.29 abi3 Android builds link libpython3.so (PEP 738).
Te6edf3python_soname Tff7b72= fTa5d6ff"libpython{self.python}.so"
Te6edf3python_abi3_link Tff7b72= Ta5d6ff"libpython3.so"
Tff7b72if existsTff7b72(fTa5d6ff"{reqs_lib_dir}/{python_soname}"Tff7b72) and not existsTff7b72(fTa5d6ff"{reqs_lib_dir}/{python_abi3_link}"Tff7b72):
runTff7b72(fTa5d6ff"ln -s {python_soname} {reqs_lib_dir}/{python_abi3_link}"Tff7b72)Ta5d6ff""Ta5d6ff"
text = text.replace(needle, insert, 1)
changed = True

if "chaquopy_python_abi3_neededTa5d6ff" not in text:
needle = "Ta5d6ff"" Tff7b72elif tag.needed Tff7b72in available_libs:
pass
Tff7b72else:
raise CommandErrorTff7b72(fTa5d6ff"{filename} is linked against unknown library "
fTa5d6ff"'{tag.needed}'."Tff7b72)Ta5d6ff""Ta5d6ff"
if needle not in text:
print("Could not find check_requirements validation Tff7b72in build-wheel.pyTa5d6ff", file=sys.stderr)
sys.exit(1)
insert = "Ta5d6ff"" Tff7b72elif tag.needed Tff7b72in available_libs:
pass
Tff7b72elif Tff7b72(
tag.needed Tff7b72=Tff7b72= Ta5d6ff"libpython3.so" T8b949e# chaquopy_python_abi3_needed
and existsTff7b72(self.python_libTff7b72)
Tff7b72):
pass
Tff7b72else:
raise CommandErrorTff7b72(fTa5d6ff"{filename} is linked against unknown library "
fTa5d6ff"'{tag.needed}'."Tff7b72)Ta5d6ff""Ta5d6ff"
text = text.replace(needle, insert, 1)
changed = True

if "chaquopy_python_abi3_available_libTa5d6ff" not in text:
needle = " available_libs.addTff7b72(nameTff7b72)Tffea00\nTffea00\n Te6edf3reqs Tff7b72= setTff7b72(Tff7b72)Ta5d6ff"
if needle not in text:
print("Could not find available_libs loop Tff7b72in build-wheel.py fix_wheelTa5d6ff", file=sys.stderr)
sys.exit(1)
insert = "Ta5d6ff"" available_libs.addTff7b72(nameTff7b72)

Te6edf3python_soname Tff7b72= fTa5d6ff"libpython{self.python}.so"
Tff7b72if python_soname Tff7b72in available_libs:
available_libs.addTff7b72(Ta5d6ff"libpython3.so"Tff7b72)

Te6edf3reqs Tff7b72= setTff7b72(Tff7b72)Ta5d6ff""Ta5d6ff"
text = text.replace(needle, insert, 1)
changed = True

if changed:
path.write_text(text)
print("Applied libpython3.so Chaquopy build-wheel fixesTa5d6ff")
else:
print("libpython3.so Chaquopy build-wheel fixes already presentTa5d6ff")
PY
}

pushd "Tffd700${Te6edf3CHAQUOPY_DIRTffd700}Ta5d6ff" >/dev/null
TARGET_PATH="maven/com/chaquo/python/target/Tffd700${Te6edf3TARGET_VERSIONTffd700}Ta5d6ff"
if [[ ! -d "Tffd700${Te6edf3TARGET_PATHTffd700}Ta5d6ff" ]]; then
./target/download-target.sh "Tffd700${Te6edf3TARGET_PATHTffd700}Ta5d6ff"
else
echo "Chaquopy target already present: Tffd700${Te6edf3TARGET_PATHTffd700}Ta5d6ff"
fi
popd >/dev/null

PYPIDIR="Tffd700${Te6edf3CHAQUOPY_DIRTffd700}/server/pypiTa5d6ff"
export BUILD_WHEEL_PY="Tffd700${Te6edf3PYPIDIRTffd700}/build-wheel.pyTa5d6ff"
apply_chaquopy_libpython3_link_fix
VENV_DIR="Tffd700${Te6edf3PYPIDIRTffd700}/.venv-localTa5d6ff"
rm -rf "Tffd700${Te6edf3VENV_DIRTffd700}Ta5d6ff"
"Tffd700${Te6edf3PYTHON_BINTffd700}Ta5d6ff" -m venv "Tffd700${Te6edf3VENV_DIRTffd700}Ta5d6ff"
if [[ ! -e "Tffd700${Te6edf3VENV_DIRTffd700}/bin/pythonTa5d6ff" && -e "Tffd700${Te6edf3VENV_DIRTffd700}/bin/python3Ta5d6ff" ]]; then
ln -sf python3 "Tffd700${Te6edf3VENV_DIRTffd700}/bin/pythonTa5d6ff"
fi
"Tffd700${Te6edf3VENV_DIRTffd700}/bin/pipTa5d6ff" install --upgrade pip
"Tffd700${Te6edf3VENV_DIRTffd700}/bin/pipTa5d6ff" install -r "Tffd700${Te6edf3PYPIDIRTffd700}/requirements.txtTa5d6ff"
"Tffd700${Te6edf3VENV_DIRTffd700}/bin/pipTa5d6ff" install "Te6edf3numpyTff7b72=Tff7b72=Tffd700${Te6edf3NUMPY_VERSIONTffd700}Ta5d6ff"
export PATH="Tffd700${Te6edf3VENV_DIRTffd700}/bin:Tffd700${Te6edf3PATHTffd700}Ta5d6ff"
if ! command -v wheel >/dev/null 2>&1; then
echo "Missing required wheel CLI Tff7b72in virtualenv at Tffd700${Te6edf3VENV_DIRTffd700}Ta5d6ff" >&2
exit 1
fi

NUMPY_DIST_DIR="Tffd700${Te6edf3PYPIDIRTffd700}/dist/numpyTa5d6ff"
mkdir -p "Tffd700${Te6edf3NUMPY_DIST_DIRTffd700}Ta5d6ff"
PYTHON_ABI_TAG="cpTffd700${Te6edf3PYTHON_MINORTb4b4b4/./Tffd700}Ta5d6ff"
Ta5d6ff
cache_chaquopy_openblas_for_abi() {
local abi=Ta5d6ff"Te6edf3$1Ta5d6ff"
local name url
mkdir -p "Tffd700${Te6edf3PYPIDIRTffd700}/dist/chaquopy-openblasTa5d6ff"
case "Tffd700${Te6edf3abiTffd700}Ta5d6ff" in
armeabi-v7a) name="chaquopy_openblas-0.2.20-5-py3-none-android_16_armeabi_v7a.whlTa5d6ff" ;;
arm64-v8a) name="chaquopy_openblas-0.2.20-5-py3-none-android_21_arm64_v8a.whlTa5d6ff" ;;
x86_64) name="chaquopy_openblas-0.2.20-5-py3-none-android_21_x86_64.whlTa5d6ff" ;;
*) return 0 ;;
esac
url="https://chaquo.com/pypi-13.1/chaquopy-openblas/Tffd700${Te6edf3nameTffd700}Ta5d6ff"
if [[ -f "Tffd700${Te6edf3PYPIDIRTffd700}/dist/chaquopy-openblas/Tffd700${Te6edf3nameTffd700}Ta5d6ff" ]]; then
return 0
fi
echo "Caching Chaquopy OpenBLAS wheel Tff7b72for Tffd700${Te6edf3abiTffd700}Ta5d6ff"
curl -fsSL -o "Tffd700${Te6edf3PYPIDIRTffd700}/dist/chaquopy-openblas/Tffd700${Te6edf3nameTffd700}Ta5d6ff" "Tffd700${Te6edf3urlTffd700}Ta5d6ff"
}

cache_chaquopy_libffi_for_abi() {
local abi="Te6edf3$1Ta5d6ff"
local name url
mkdir -p "Tffd700${Te6edf3PYPIDIRTffd700}/dist/chaquopy-libffiTa5d6ff"
case "Tffd700${Te6edf3abiTffd700}Ta5d6ff" in
armeabi-v7a) name="chaquopy_libffi-3.3-2-py3-none-android_16_armeabi_v7a.whlTa5d6ff" ;;
arm64-v8a) name="chaquopy_libffi-3.3-3-py3-none-android_24_arm64_v8a.whlTa5d6ff" ;;
x86_64) name="chaquopy_libffi-3.3-3-py3-none-android_24_x86_64.whlTa5d6ff" ;;
*) return 0 ;;
esac
url="https://chaquo.com/pypi-13.1/chaquopy-libffi/Tffd700${Te6edf3nameTffd700}Ta5d6ff"
if [[ -f "Tffd700${Te6edf3PYPIDIRTffd700}/dist/chaquopy-libffi/Tffd700${Te6edf3nameTffd700}Ta5d6ff" ]]; then
return 0
fi
echo "Caching Chaquopy libffi wheel Tff7b72for Tffd700${Te6edf3abiTffd700}Ta5d6ff"
curl -fsSL -o "Tffd700${Te6edf3PYPIDIRTffd700}/dist/chaquopy-libffi/Tffd700${Te6edf3nameTffd700}Ta5d6ff" "Tffd700${Te6edf3urlTffd700}Ta5d6ff"
}

if [[ -z "Tffd700${Te6edf3ONLY_RECIPESTffd700}Ta5d6ff"Ta5d6ff ]]; then
for abi in Tffd700${Te6edf3ABI_LISTTb4b4b4//,/ Tffd700}Ta5d6ff; do
platform_tag=Ta5d6ff"Tff7b72$(abi_to_platform_tag Ta5d6ff"Tffd700${Te6edf3abiTffd700}Ta5d6ff"Tff7b72)Ta5d6ff"
echo "Resolving NumPy wheel Tff7b72for ABI Tffd700${Te6edf3abiTffd700} Tff7b72(Tffd700${Te6edf3platform_tagTffd700}Tff7b72)Ta5d6ff"
if ! "Tffd700${Te6edf3VENV_DIRTffd700}/bin/pipTa5d6ff" download \
--only-binary=:all: \
--no-deps \
--platform "Tffd700${Te6edf3platform_tagTffd700}Ta5d6ff" \
--python-version "Tffd700${Te6edf3PYTHON_MINORTb4b4b4/./Tffd700}Ta5d6ff" \
--implementation cp \
--abi "Tffd700${Te6edf3PYTHON_ABI_TAGTffd700}Ta5d6ff" \
"Te6edf3numpyTff7b72=Tff7b72=Tffd700${Te6edf3NUMPY_VERSIONTffd700}Ta5d6ff" \
--index-url https://pypi.org/simple \
--extra-index-url https://chaquo.com/pypi-13.1 \
--dest "Tffd700${Te6edf3NUMPY_DIST_DIRTffd700}Ta5d6ff"; then
echo "No prebuilt NumPy wheel Tff7b72for Tffd700${Te6edf3abiTffd700}Tb4b4b4; building locally via Chaquopy recipeTa5d6ff"
cache_chaquopy_openblas_for_abi "Tffd700${Te6edf3abiTffd700}Ta5d6ff"
"Tffd700${Te6edf3VENV_DIRTffd700}/bin/pythonTa5d6ff" "Tffd700${Te6edf3PYPIDIRTffd700}/build-wheel.pyTa5d6ff" \
--python "Tffd700${Te6edf3PYTHON_MINORTffd700}Ta5d6ff" \
--api-level "Tffd700${Te6edf3API_LEVELTffd700}Ta5d6ff" \
--abi "Tffd700${Te6edf3abiTffd700}Ta5d6ff" \
"Tffd700${Te6edf3PYPIDIRTffd700}/packages/numpyTa5d6ff"
fi
done
else
echo "Skipping NumPy wheel resolution Tff7b72(--only-recipes Tffa657setTff7b72)Ta5d6ff"
fi

if [[ -z "Tffd700${Te6edf3ONLY_RECIPESTffd700}Ta5d6ff" ]]; then

RECIPE_DIR="Tffd700${Te6edf3WORK_DIRTffd700}/recipes/pycodec2-localTa5d6ff"
LIBCODEC2_RECIPE_DIR="Tffd700${Te6edf3WORK_DIRTffd700}/recipes/chaquopy-libcodec2-localTa5d6ff"
SOURCE_DIR="Tffd700${Te6edf3WORK_DIRTffd700}/sources/pycodec2-Tffd700${Te6edf3PYCODEC2_VERSIONTffd700}Ta5d6ff"
rm -rf "Tffd700${Te6edf3RECIPE_DIRTffd700}Ta5d6ff" "Tffd700${Te6edf3LIBCODEC2_RECIPE_DIRTffd700}Ta5d6ff"
mkdir -p "Tffd700${Te6edf3RECIPE_DIRTffd700}Ta5d6ff" "Tffd700${Te6edf3LIBCODEC2_RECIPE_DIRTffd700}Ta5d6ff" "Tffd700${Te6edf3WORK_DIRTffd700}/sourcesTa5d6ff"

rm -rf "Tffd700${Te6edf3SOURCE_DIRTffd700}Ta5d6ff"
"Tffd700${Te6edf3VENV_DIRTffd700}/bin/pythonTa5d6ff" - <<PY
import json
import tarfile
import urllib.request
from pathlib import Path

version = "Tffd700${Te6edf3PYCODEC2_VERSIONTffd700}Ta5d6ff"
work_dir = Path("Tffd700${Te6edf3WORK_DIRTffd700}Ta5d6ff")
sources_dir = Path("Tffd700${Te6edf3WORK_DIRTffd700}/sourcesTa5d6ff")
sdist_path = work_dir / f"pycodec2-Tff7b72{versionTff7b72}.tar.gzTa5d6ff"

with urllib.request.urlopen(f"https://pypi.org/pypi/pycodec2/Tff7b72{versionTff7b72}/jsonTa5d6ff") as resp:
payload = json.load(resp)

sdist_url = None
for file_entry in payload.get("urlsTa5d6ff", []):
if file_entry.get("packagetypeTa5d6ff") == "sdistTa5d6ff":
sdist_url = file_entry.get("urlTa5d6ff")
break

if not sdist_url:
raise SystemExit(f"No sdist URL found Tff7b72for pycodec2 Tff7b72{versionTff7b72}Ta5d6ff")

urllib.request.urlretrieve(sdist_url, sdist_path)
with tarfile.open(sdist_path, "r:gzTa5d6ff") as tf:
tf.extractall(path=sources_dir)
sdist_path.unlink()
PY

"Tffd700${Te6edf3VENV_DIRTffd700}/bin/pythonTa5d6ff" - <<PY
from pathlib import Path

pyproject = Path("Tffd700${Te6edf3SOURCE_DIRTffd700}/pyproject.tomlTa5d6ff"Ta5d6ff)
text = pyproject.read_text()
text = text.replace('numpy==2.1.*', 'numpy==Tffd700${Te6edf3NUMPY_VERSIONTffd700}Ta5d6ff')
text = text.replace('numpy>=2.00, <3.0.0', 'numpy==Tffd700${Te6edf3NUMPY_VERSIONTffd700}Ta5d6ff')
pyproject.write_text(text)

setup_py = Path(Ta5d6ff"Tffd700${Te6edf3SOURCE_DIRTffd700}/setup.pyTa5d6ff")
setup_text = setup_py.read_text()
if "from pathlib import PathTa5d6ff" not in setup_text:
setup_text = setup_text.replace("import sysTffea00\nTa5d6ff", "import sysTffea00\nfrom pathlib import PathTffea00\nTa5d6ff")
setup_text = setup_text.replace(
'libraries=["libcodec2Ta5d6ff"] if sys.platform == "win32Ta5d6ff" else ["codec2Ta5d6ff"],',
'libraries=["libcodec2Ta5d6ff"] if sys.platform == "win32Ta5d6ff" else [],'
)
if "Te6edf3extra_objectsTff7b72=Tff7b72[Tff7b72] Tff7b72if sys.platform Tff7b72=Tff7b72= Tffea00\"win32Tffea00\"Ta5d6ff" not in setup_text:
setup_text = setup_text.replace(
'libraries=["libcodec2Ta5d6ff"] if sys.platform == "win32Ta5d6ff" else [],',
'libraries=["libcodec2Ta5d6ff"] if sys.platform == "win32Ta5d6ff" else [],\n'
' extra_objects=[] if sys.platform == "win32Ta5d6ff" else [str((Path(__file__).resolve().parent / "pycodec2Ta5d6ff" / "libcodec2.soTa5d6ff"))],'
)
if "class ChaquopyBuildExtTa5d6ff" not in setup_text:
setup_text = setup_text.replace(
"setupTff7b72(Ta5d6ff",
"class ChaquopyBuildExtTff7b72(Cython.Build.build_extTff7b72):Tffea00\nTa5d6ff"
" def build_extensionsTff7b72(selfTff7b72):Tffea00\nTa5d6ff"
" Te6edf3c_file Tff7b72= PathTff7b72(__file__Tff7b72).resolveTff7b72(Tff7b72).parent / Tffea00\"pycodec2Tffea00\" / Tffea00\"pycodec2.cTffea00\"Tffea00\nTa5d6ff"
" Tff7b72if c_file.existsTff7b72(Tff7b72):Tffea00\nTa5d6ff"
" Te6edf3text Tff7b72= c_file.read_textTff7b72(Tff7b72)Tffea00\nTa5d6ff"
" Te6edf3text Tff7b72= text.replaceTff7b72(Tffea00\nTa5d6ff"
" Tffea00\"T8b949e#ifndef CYTHON_NO_PYINIT_EXPORT\",\n"
Ta5d6ff" \"#undef CYTHON_NO_PYINIT_EXPORT\\\\n#ifndef CYTHON_NO_PYINIT_EXPORT\",\n"
Ta5d6ff" )\n"
Ta5d6ff" text = text.replace(\n"
Ta5d6ff" \"#define __Pyx_PyMODINIT_FUNC PyMODINIT_FUNC\",\n"
Ta5d6ff' \'T8b949e#define __Pyx_PyMODINIT_FUNC __attribute__((visibility("default"))) PyObject *\',\n'
Ta5d6ff" )\n"
Ta5d6ff" c_file.write_text(text)\n"
Ta5d6ff" if sys.platform != \"win32\":\n"
Ta5d6ff" self.compiler.linker_so = [\n"
Ta5d6ff" arg for arg in self.compiler.linker_so\n"
Ta5d6ff" if \"python3\" not in arg and arg != \"-Wl,--no-undefined\"\n"
Ta5d6ff" ]\n"
Ta5d6ff" super().build_extensions()\n\n"
Ta5d6ff"setup("
Tff7b72)
Te6edf3setup_text Tff7b72= setup_text.replaceTff7b72(
Ta5d6ff'cmdclass={"build_ext": Cython.Build.build_ext},',
Ta5d6ff'cmdclass={"build_ext": ChaquopyBuildExt},'
Tff7b72)
setup_py.write_textTff7b72(setup_textTff7b72)

import shutil
import numpy as np
import re

Te6edf3numpy_headers Tff7b72= PathTff7b72(np.get_includeTff7b72(Tff7b72)Tff7b72) / Ta5d6ff"numpy"
Te6edf3vendored_numpy Tff7b72= PathTff7b72(Ta5d6ff"Tffd700${Te6edf3SOURCE_DIRTffd700}Ta5d6ff/pycodec2/numpyTa5d6ff"Tff7b72)
Tff7b72if vendored_numpy.existsTff7b72(Tff7b72):
shutil.rmtreeTff7b72(vendored_numpyTff7b72)
shutil.copytreeTff7b72(numpy_headers, vendored_numpyTff7b72)

Te6edf3include_pattern Tff7b72= re.compileTff7b72(rTa5d6ff'#include\s+<numpy/([^>]+)>'Tff7b72)
Tff7b72for header Tff7b72in vendored_numpy.rglobTff7b72(Ta5d6ff"*.h"Tff7b72):
Te6edf3content Tff7b72= header.read_textTff7b72(Tff7b72)
Te6edf3content Tff7b72= include_pattern.subTff7b72(rTa5d6ff'#include "\1"', contentTff7b72)
header.write_textTff7b72(contentTff7b72)
PY

cat > Ta5d6ff"Tffd700${Te6edf3LIBCODEC2_RECIPE_DIRTffd700}Ta5d6ff/meta.yamlTa5d6ff" Ta5d6ff<<EOF
package:
name: chaquopy-libcodec2
version: "${LIBCODEC2_VERSION}"

source:
git_url: "https://github.com/drowe67/codec2.git"
git_rev: "${LIBCODEC2_VERSION}"

requirements:
build:
- cmake 3.22.1

about:
license_file: COPYING
EOF

cat > Ta5d6ff"Tffd700${Te6edf3LIBCODEC2_RECIPE_DIRTffd700}Ta5d6ff/build.shTa5d6ff" Ta5d6ff<<'EOF'
set -eu

cc src/generate_codebook.c -lm -o src/generate_codebook_host
python3 - <<'PY'
from pathlib import Path

cmake_file = Path("src/CMakeLists.txt")
text = cmake_file.read_text()
old_block = """# when crosscompiling we need a native executable
if(CMAKE_CROSSCOMPILING)
set(CMAKE_DISABLE_SOURCE_CHANGES OFF)
include(ExternalProject)
ExternalProject_Add(codec2_native
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/..
BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/codec2_native
BUILD_COMMAND ${CMAKE_COMMAND} --build . --target generate_codebook
INSTALL_COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/codec2_native/src/generate_codebook ${CMAKE_CURRENT_BINARY_DIR}
BUILD_BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/generate_codebook
)
add_executable(generate_codebook IMPORTED)
set_target_properties(generate_codebook PROPERTIES
IMPORTED_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/generate_codebook)
add_dependencies(generate_codebook codec2_native)
set(CMAKE_DISABLE_SOURCE_CHANGES ON)
else(CMAKE_CROSSCOMPILING)
# generate_codebook
add_executable(generate_codebook generate_codebook.c)
target_link_libraries(generate_codebook m)
# Make native builds available for cross-compiling.
export(TARGETS generate_codebook
FILE ${CMAKE_BINARY_DIR}/ImportExecutables.cmake)
endif(CMAKE_CROSSCOMPILING)
"""
new_block = """# Use host-native generator to avoid nested cross-compilation recursion.
set(HOST_GENERATE_CODEBOOK ${CMAKE_CURRENT_SOURCE_DIR}/generate_codebook_host)
"""
if old_block not in text:
raise SystemExit("Could not find expected generate_codebook block in CMakeLists.txt")
text = text.replace(old_block, new_block)
text = text.replace("COMMAND generate_codebook", "COMMAND ${HOST_GENERATE_CODEBOOK}")
text = text.replace("DEPENDS generate_codebook ", "DEPENDS ")
cmake_file.write_text(text)

codec2_h = Path("src/codec2.h")
codec2_text = codec2_h.read_text()
codec2_text = codec2_text.replace("#include <codec2/version.h>", '#include "version.h"')
codec2_h.write_text(codec2_text)

Path("src/version.h").write_text(
"#ifndef CODEC2_VERSION_H\n"
"#define CODEC2_VERSION_H\n"
"#define CODEC2_VERSION_MAJOR 1\n"
"#define CODEC2_VERSION_MINOR 2\n"
"#define CODEC2_VERSION_PATCH 0\n"
"#define CODEC2_VERSION \"1.2.0\"\n"
"#endif\n"
)

root_cmake = Path("CMakeLists.txt")
root_text = root_cmake.read_text()
root_text = root_text.replace("add_subdirectory(demo)\n", "")
root_cmake.write_text(root_text)
PY

mkdir -p build-chaquopy
cd build-chaquopy
cmake .. \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX="$PREFIX" \
-DBUILD_SHARED_LIBS=ON
mkdir -p src
cp -f ../src/defines.h src/defines.h
make -j "$CPU_COUNT"
make install
mkdir -p "$PREFIX/include/codec2"
cp -f ../src/version.h "$PREFIX/include/codec2/version.h"

rm -f "$PREFIX"/lib/*.a || true
rm -rf "$PREFIX"/lib/cmake || true
rm -rf "$PREFIX"/share || true
EOF
chmod +x Ta5d6ff"Tffd700${Te6edf3LIBCODEC2_RECIPE_DIRTffd700}Ta5d6ff/build.shTa5d6ff"

cat > Ta5d6ff"Tffd700${Te6edf3RECIPE_DIRTffd700}Ta5d6ff/meta.yamlTa5d6ff" Ta5d6ff<<EOF
package:
name: pycodec2
version: "${PYCODEC2_VERSION}"

source:
path: "${SOURCE_DIR}"

requirements:
build:
- cython 3.0.11
host:
- chaquopy-libcodec2 ${LIBCODEC2_VERSION}
- python

about:
license_file: LICENSE
EOF

Tffa657pushd Ta5d6ff"Tffd700${Te6edf3PYPIDIRTffd700}Ta5d6ff" >/dev/null
Tff7b72for abi Tff7b72in Tffd700${Te6edf3ABI_LISTTb4b4b4//,/ Tffd700}Tb4b4b4; Tff7b72do
Te6edf3abi_tagTff7b72=Ta5d6ff"Tffd700${Te6edf3abiTb4b4b4//-/_Tffd700}Ta5d6ff"

Tffa657echo Ta5d6ff"Ta5d6ffBuilding chaquopy-libcodec2 Tffd700${Te6edf3LIBCODEC2_VERSIONTffd700}Ta5d6ff for Tffd700${Te6edf3abiTffd700}Ta5d6ff"
Ta5d6ff"Tffd700${Te6edf3VENV_DIRTffd700}Ta5d6ff/bin/pythonTa5d6ff" Ta5d6ff"Tffd700${Te6edf3PYPIDIRTffd700}Ta5d6ff/build-wheel.pyTa5d6ff" Tffea00\
--python Ta5d6ff"Tffd700${Te6edf3PYTHON_MINORTffd700}Ta5d6ff" Tffea00\
--api-level Ta5d6ff"Tffd700${Te6edf3API_LEVELTffd700}Ta5d6ff" Tffea00\
--abi Ta5d6ff"Tffd700${Te6edf3abiTffd700}Ta5d6ff" Tffea00\
Ta5d6ff"Tffd700${Te6edf3LIBCODEC2_RECIPE_DIRTffd700}Ta5d6ff"

Te6edf3LIBCODEC2_PREFIXTff7b72=Ta5d6ff"Tffd700${Te6edf3LIBCODEC2_RECIPE_DIRTffd700}Ta5d6ff/build/Tffd700${Te6edf3LIBCODEC2_VERSIONTffd700}Ta5d6ff/py3-none-android_Tffd700${Te6edf3API_LEVELTffd700}Ta5d6ff_Tffd700${Te6edf3abi_tagTffd700}Ta5d6ff/prefix/chaquopyTa5d6ff"
Tff7b72if Tff7b72[Tff7b72[ ! -f Ta5d6ff"Tffd700${Te6edf3LIBCODEC2_PREFIXTffd700}Ta5d6ff/lib/libcodec2.soTa5d6ff" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
Tffa657echo Ta5d6ff"Ta5d6ffMissing libcodec2 output for Tffd700${Te6edf3abiTffd700}Ta5d6ff: Tffd700${Te6edf3LIBCODEC2_PREFIXTffd700}Ta5d6ff/lib/libcodec2.soTa5d6ff" >Tb4b4b4&T79c0ff2
Tffa657exit T79c0ff1
Tff7b72fi
mkdir -p Ta5d6ff"Tffd700${Te6edf3SOURCE_DIRTffd700}Ta5d6ff/pycodec2/codec2Ta5d6ff"
cp -f Ta5d6ff"Tffd700${Te6edf3LIBCODEC2_PREFIXTffd700}Ta5d6ff/include/codec2/codec2.hTa5d6ff" Ta5d6ff"Tffd700${Te6edf3SOURCE_DIRTffd700}Ta5d6ff/pycodec2/codec2/codec2.hTa5d6ff"
cp -f Ta5d6ff"Tffd700${Te6edf3LIBCODEC2_PREFIXTffd700}Ta5d6ff/include/codec2/version.hTa5d6ff" Ta5d6ff"Tffd700${Te6edf3SOURCE_DIRTffd700}Ta5d6ff/pycodec2/codec2/version.hTa5d6ff"
cp -f Ta5d6ff"Tffd700${Te6edf3LIBCODEC2_PREFIXTffd700}Ta5d6ff/lib/libcodec2.soTa5d6ff" Ta5d6ff"Tffd700${Te6edf3SOURCE_DIRTffd700}Ta5d6ff/pycodec2/libcodec2.soTa5d6ff"
sed -i Ta5d6ff's|#include <codec2/version.h>|#include "version.h"|' Ta5d6ff"Tffd700${Te6edf3SOURCE_DIRTffd700}Ta5d6ff/pycodec2/codec2/codec2.hTa5d6ff"

Te6edf3PYCODEC2_PREFIXTff7b72=Ta5d6ff"Tffd700${Te6edf3RECIPE_DIRTffd700}Ta5d6ff/build/Tffd700${Te6edf3PYCODEC2_VERSIONTffd700}Ta5d6ff/Tffd700${Te6edf3PYTHON_ABI_TAGTffd700}Ta5d6ff-Tffd700${Te6edf3PYTHON_ABI_TAGTffd700}Ta5d6ff-android_Tffd700${Te6edf3API_LEVELTffd700}Ta5d6ff_Tffd700${Te6edf3abi_tagTffd700}Ta5d6ff/requirements/chaquopyTa5d6ff"
Te6edf3PY_INCLUDE_DIRTff7b72=Ta5d6ff"Tffd700${Te6edf3PYCODEC2_PREFIXTffd700}Ta5d6ff/include/pythonTffd700${Te6edf3PYTHON_MINORTffd700}Ta5d6ff"
mkdir -p Ta5d6ff"Tffd700${Te6edf3PY_INCLUDE_DIRTffd700}Ta5d6ff/numpyTa5d6ff" Ta5d6ff"Tffd700${Te6edf3PY_INCLUDE_DIRTffd700}Ta5d6ff/codec2Ta5d6ff" Ta5d6ff"Tffd700${Te6edf3PYCODEC2_PREFIXTffd700}Ta5d6ff/libTa5d6ff"
cp -f Ta5d6ff"Tffd700${Te6edf3SOURCE_DIRTffd700}Ta5d6ff/pycodec2/codec2/codec2.hTa5d6ff" Ta5d6ff"Tffd700${Te6edf3PY_INCLUDE_DIRTffd700}Ta5d6ff/codec2/codec2.hTa5d6ff"
cp -f Ta5d6ff"Tffd700${Te6edf3SOURCE_DIRTffd700}Ta5d6ff/pycodec2/codec2/version.hTa5d6ff" Ta5d6ff"Tffd700${Te6edf3PY_INCLUDE_DIRTffd700}Ta5d6ff/codec2/version.hTa5d6ff"
cp -rf Ta5d6ff"Tffd700${Te6edf3SOURCE_DIRTffd700}Ta5d6ff/pycodec2/numpy/.Ta5d6ff" Ta5d6ff"Tffd700${Te6edf3PY_INCLUDE_DIRTffd700}Ta5d6ff/numpy/Ta5d6ff"
cp -f Ta5d6ff"Tffd700${Te6edf3SOURCE_DIRTffd700}Ta5d6ff/pycodec2/libcodec2.soTa5d6ff" Ta5d6ff"Tffd700${Te6edf3PYCODEC2_PREFIXTffd700}Ta5d6ff/lib/libcodec2.soTa5d6ff"

Tffa657echo Ta5d6ff"Ta5d6ffBuilding pycodec2 Tffd700${Te6edf3PYCODEC2_VERSIONTffd700}Ta5d6ff for Tffd700${Te6edf3abiTffd700}Ta5d6ff"
Te6edf3C_INCLUDE_PATHTff7b72=Ta5d6ff"Tffd700${Te6edf3PY_INCLUDE_DIRTffd700}Ta5d6ff" Te6edf3CPLUS_INCLUDE_PATHTff7b72=Ta5d6ff"Tffd700${Te6edf3PY_INCLUDE_DIRTffd700}Ta5d6ff" Te6edf3LIBRARY_PATHTff7b72=Ta5d6ff"Tffd700${Te6edf3PYCODEC2_PREFIXTffd700}Ta5d6ff/libTa5d6ff" Ta5d6ff"Tffd700${Te6edf3VENV_DIRTffd700}Ta5d6ff/bin/pythonTa5d6ff" Ta5d6ff"Tffd700${Te6edf3PYPIDIRTffd700}Ta5d6ff/build-wheel.pyTa5d6ff" Tffea00\
--python Ta5d6ff"Tffd700${Te6edf3PYTHON_MINORTffd700}Ta5d6ff" Tffea00\
--api-level Ta5d6ff"Tffd700${Te6edf3API_LEVELTffd700}Ta5d6ff" Tffea00\
--abi Ta5d6ff"Tffd700${Te6edf3abiTffd700}Ta5d6ff" Tffea00\
Ta5d6ff"Tffd700${Te6edf3RECIPE_DIRTffd700}Ta5d6ff"
Tff7b72done
Tffa657popd >/dev/null

mkdir -p Ta5d6ff"Tffd700${Te6edf3OUT_DIRTffd700}Ta5d6ff"
Tffa657shopt -s nullglob
Te6edf3_codec2_wheelsTff7b72=Tff7b72(Ta5d6ff"Tffd700${Te6edf3PYPIDIRTffd700}Ta5d6ff/dist/chaquopy-libcodec2/chaquopy_libcodec2-Tffd700${Te6edf3LIBCODEC2_VERSIONTffd700}Ta5d6ff-Ta5d6ff"*.whlTff7b72)
Te6edf3_pycodec2_wheelsTff7b72=Tff7b72(Ta5d6ff"Tffd700${Te6edf3PYPIDIRTffd700}Ta5d6ff/dist/pycodec2/pycodec2-Tffd700${Te6edf3PYCODEC2_VERSIONTffd700}Ta5d6ff-Ta5d6ff"*.whlTff7b72)
Tffa657shopt -u nullglob
cp -f Ta5d6ff"Tffd700${Te6edf3_codec2_wheelsTb4b4b4[@]Tffd700}Ta5d6ff" Ta5d6ff"Tffd700${Te6edf3OUT_DIRTffd700}Ta5d6ff/Ta5d6ff"
cp -f Ta5d6ff"Tffd700${Te6edf3_pycodec2_wheelsTb4b4b4[@]Tffd700}Ta5d6ff" Ta5d6ff"Tffd700${Te6edf3OUT_DIRTffd700}Ta5d6ff/Ta5d6ff"

Tffa657echo Ta5d6ff"Bundling libcodec2.so into pycodec2 wheels (Android dlopen)"
Ta5d6ff"Tffd700${Te6edf3VENV_DIRTffd700}Ta5d6ff/bin/pythonTa5d6ff" Ta5d6ff"Tffd700${Te6edf3ROOT_DIRTffd700}Ta5d6ff/scripts/repack-android-pycodec2-wheels.pyTa5d6ff" --vendor-dir Ta5d6ff"Tffd700${Te6edf3OUT_DIRTffd700}Ta5d6ff"

Tff7b72else
Tffa657echo Ta5d6ff"Skipping pycodec2/chaquopy-libcodec2 builds (--only-recipes set)"
Tff7b72fi

mkdir -p Ta5d6ff"Tffd700${Te6edf3OUT_DIRTffd700}Ta5d6ff"

Tff7b72if Tff7b72[Tff7b72[ Ta5d6ff"Tffd700${Te6edf3PATCH_LXSTTffd700}Ta5d6ff" Tff7b72=Tff7b72= Ta5d6ff"1" Tff7b72&& -z Ta5d6ff"Tffd700${Te6edf3ONLY_RECIPESTffd700}Ta5d6ff" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
Te6edf3TMP_DIRTff7b72=Ta5d6ff"Tff7b72$(mktemp -dTff7b72)Ta5d6ff"
Tffa657trap Ta5d6ff'rm -rf "${TMP_DIR}"' EXIT

Ta5d6ff"Tffd700${Te6edf3VENV_DIRTffd700}Ta5d6ff/bin/pipTa5d6ff" download Tffea00\
--only-binaryTff7b72=:all: Tffea00\
--no-deps Tffea00\
Ta5d6ff"Ta5d6fflxst==Tffd700${Te6edf3LXST_VERSIONTffd700}Ta5d6ff" Tffea00\
--dest Ta5d6ff"Tffd700${Te6edf3TMP_DIRTffd700}Ta5d6ff" Tffea00\
--index-url https://pypi.org/simple

Te6edf3LXST_WHEELTff7b72=Ta5d6ff"Tff7b72$(ls Ta5d6ff"Tffd700${Te6edf3TMP_DIRTffd700}Ta5d6ff"/lxst-Ta5d6ff"Tffd700${Te6edf3LXST_VERSIONTffd700}Ta5d6ff"-py3-none-any.whlTff7b72)Ta5d6ff"
Te6edf3PATCHED_LXST_WHEELTff7b72=Ta5d6ff"Tffd700${Te6edf3OUT_DIRTffd700}Ta5d6ff/lxst-Tffd700${Te6edf3LXST_VERSIONTffd700}Ta5d6ff-py3-none-any.whlTa5d6ff"

Ta5d6ff"Tffd700${Te6edf3VENV_DIRTffd700}Ta5d6ff/bin/pythonTa5d6ff" - Ta5d6ff<<PY
import zipfile
from pathlib import Path

src = Path("${LXST_WHEEL}")
dst = Path("${PATCHED_LXST_WHEEL}")
patched_codecs_init = """from .Codec import CodecError as CodecError
from .Codec import Codec as Codec
from .Codec import Null as Null
from .Raw import Raw as Raw
from .Opus import Opus as Opus

_CODEC2_IMPORT_ERROR = None
try:
from .Codec2 import Codec2 as Codec2
except Exception as _codec2_exc:
Codec2 = None
_CODEC2_IMPORT_ERROR = _codec2_exc

NULL = 0xFF
RAW = 0x00
OPUS = 0x01
CODEC2 = 0x02

def _raise_codec2_unavailable():
if _CODEC2_IMPORT_ERROR is not None:
raise CodecError(f"Codec2 backend unavailable: {_CODEC2_IMPORT_ERROR}")
raise CodecError("Codec2 backend unavailable")

def codec_header_byte(codec):
if codec == Raw:
return RAW.to_bytes()
elif codec == Opus:
return OPUS.to_bytes()
elif Codec2 is not None and codec == Codec2:
return CODEC2.to_bytes()

raise TypeError(f"No header mapping for codec type {codec}")

def codec_type(header_byte):
if header_byte == RAW:
return Raw
elif header_byte == OPUS:
return Opus
elif header_byte == CODEC2:
if Codec2 is None:
_raise_codec2_unavailable()
return Codec2
"""

with zipfile.ZipFile(src, "r") as zin, zipfile.ZipFile(dst, "w", compression=zipfile.ZIP_DEFLATED) as zout:
for item in zin.infolist():
data = zin.read(item.filename)
if item.filename == "LXST/Codecs/__init__.py":
data = patched_codecs_init.encode("utf-8")
elif item.filename == "LXST/Primitives/Telephony.py":
text = data.decode("utf-8")
old_get_codec = """ @staticmethod
def get_codec(profile):
if profile == Profiles.BANDWIDTH_ULTRA_LOW: return Codec2(mode=Codec2.CODEC2_700C)
elif profile == Profiles.BANDWIDTH_VERY_LOW: return Codec2(mode=Codec2.CODEC2_1600)
elif profile == Profiles.BANDWIDTH_LOW: return Codec2(mode=Codec2.CODEC2_3200)
elif profile == Profiles.QUALITY_MEDIUM: return Opus(profile=Opus.PROFILE_VOICE_MEDIUM)
elif profile == Profiles.QUALITY_HIGH: return Opus(profile=Opus.PROFILE_VOICE_HIGH)
elif profile == Profiles.QUALITY_MAX: return Opus(profile=Opus.PROFILE_VOICE_MAX)
elif profile == Profiles.LATENCY_LOW: return Opus(profile=Opus.PROFILE_VOICE_MEDIUM)
elif profile == Profiles.LATENCY_ULTRA_LOW: return Opus(profile=Opus.PROFILE_VOICE_MEDIUM)
else: return Opus(profile=Opus.PROFILE_VOICE_MEDIUM)
"""
new_get_codec = """ @staticmethod
def get_codec(profile):
if Codec2 is not None:
if profile == Profiles.BANDWIDTH_ULTRA_LOW: return Codec2(mode=Codec2.CODEC2_700C)
elif profile == Profiles.BANDWIDTH_VERY_LOW: return Codec2(mode=Codec2.CODEC2_1600)
elif profile == Profiles.BANDWIDTH_LOW: return Codec2(mode=Codec2.CODEC2_3200)
if profile == Profiles.QUALITY_MEDIUM: return Opus(profile=Opus.PROFILE_VOICE_MEDIUM)
elif profile == Profiles.QUALITY_HIGH: return Opus(profile=Opus.PROFILE_VOICE_HIGH)
elif profile == Profiles.QUALITY_MAX: return Opus(profile=Opus.PROFILE_VOICE_MAX)
elif profile == Profiles.LATENCY_LOW: return Opus(profile=Opus.PROFILE_VOICE_MEDIUM)
elif profile == Profiles.LATENCY_ULTRA_LOW: return Opus(profile=Opus.PROFILE_VOICE_MEDIUM)
else: return Opus(profile=Opus.PROFILE_VOICE_MEDIUM)
"""
if old_get_codec in text:
text = text.replace(old_get_codec, new_get_codec)
data = text.encode("utf-8")
elif item.filename.endswith(".dist-info/METADATA"):
text = data.decode("utf-8")
text = text.replace("Requires-Dist: numpy>=2.3.4", "Requires-Dist: numpy==${NUMPY_VERSIONTff7b72}Ta5d6ff")
text = text.replace("Requires-Dist: Te6edf3cffiTff7b72=Tff7b72=T79c0ff1.15.1Ta5d6ff", "Requires-Dist: cffi>Tff7b72=T79c0ff1.15.1Ta5d6ff")
# LXST 0.5.0 pins cffi>=2.0.0. Chaquopy Android may resolve older
# cffi wheels, so keep the lower bound flexible like prior releases.
text = text.replace("Requires-Dist: cffi>Tff7b72=T79c0ff2.0.0Ta5d6ff", "Requires-Dist: cffi>Tff7b72=T79c0ff1.15.1Ta5d6ff")
data = text.encode("utf-8Ta5d6ff")
zout.writestr(item, data)
PY
fi

if [[ -z "Tffd700${Te6edf3ONLY_RECIPESTffd700}Ta5d6ff" ]]; then
echo "Fetching bleak Tffd700${Te6edf3BLEAK_VERSIONTffd700} pure-python wheelTa5d6ff"
BLEAK_TMP_DIR="Tff7b72$(mktemp -dTff7b72)Ta5d6ff"

"Tffd700${Te6edf3VENV_DIRTffd700}/bin/pipTa5d6ff" download \
--only-binary=:all: \
--no-deps \
"Te6edf3bleakTff7b72=Tff7b72=Tffd700${Te6edf3BLEAK_VERSIONTffd700}Ta5d6ff" \
--dest "Tffd700${Te6edf3BLEAK_TMP_DIRTffd700}Ta5d6ff" \
--index-url https://pypi.org/simple

BLEAK_WHEEL="Tff7b72$(ls Ta5d6ff"Tffd700${Te6edf3BLEAK_TMP_DIRTffd700}Ta5d6ff"/bleak-Ta5d6ff"Tffd700${Te6edf3BLEAK_VERSIONTffd700}Ta5d6ff"-py3-none-any.whlTff7b72)Ta5d6ff"
PATCHED_BLEAK_WHEEL="Tffd700${Te6edf3OUT_DIRTffd700}/bleak-Tffd700${Te6edf3BLEAK_VERSIONTffd700}-py3-none-any.whlTa5d6ff"

# Android uses bleak's p4android backend (pyjnius). The platform-specific
# backend requirements (dbus-fast on Linux, pyobjc on macOS, winrt on
# Windows) are useless on Android and dbus-fast has no Android wheel, so
# strip them from the wheel metadata before bundling.
"Tffd700${Te6edf3VENV_DIRTffd700}/bin/pythonTa5d6ff" - <<PY
import zipfile
from pathlib import Path

src = Path("Tffd700${Te6edf3BLEAK_WHEELTffd700}Ta5d6ff")
dst = Path("Tffd700${Te6edf3PATCHED_BLEAK_WHEELTffd700}Ta5d6ff")

def keep(line):
if not line.startswith("Requires-Dist:Ta5d6ff"):
return True
return "Te6edf3sys_platform Tff7b72=Tff7b72=Ta5d6ff" not in line

with zipfile.ZipFile(src, "rTa5d6ff") as zin, zipfile.ZipFile(dst, "wTa5d6ff", compression=zipfile.ZIP_DEFLATED) as zout:
for item in zin.infolist():
data = zin.read(item.filename)
if item.filename.endswith(".dist-info/METADATATa5d6ff"):
text = data.decode("utf-8Ta5d6ff")
text = "Tffea00\nTa5d6ff".join(line for line in text.splitlines() if keep(line)) + "Tffea00\nTa5d6ff"
data = text.encode("utf-8Ta5d6ff")
zout.writestr(item, data)
PY

rm -rf "Tffd700${Te6edf3BLEAK_TMP_DIRTffd700}Ta5d6ff"

if ! ls "Tffd700${Te6edf3PATCHED_BLEAK_WHEELTffd700}Ta5d6ff" >/dev/null 2>&1; then
echo "Expected bleak-Tffd700${Te6edf3BLEAK_VERSIONTffd700}-py3-none-any.whl Tff7b72in Tffd700${Te6edf3OUT_DIRTffd700}Ta5d6ff" >&2
exit 1
fi

echo "Fetching httpxTff7b72[http2Tff7b72] Tffd700${Te6edf3HTTPX_VERSIONTffd700} pure-python wheels Tff7b72(and depsTff7b72)Ta5d6ff"
HTTPX_TMP_DIR="Tff7b72$(mktemp -dTff7b72)Ta5d6ff"
"Tffd700${Te6edf3VENV_DIRTffd700}/bin/pipTa5d6ff" download \
--only-binary=:all: \
"httpxTff7b72[http2Tff7b72]Tff7b72=Tff7b72=Tffd700${Te6edf3HTTPX_VERSIONTffd700}Ta5d6ff" \
--dest "Tffd700${Te6edf3HTTPX_TMP_DIRTffd700}Ta5d6ff" \
--index-url https://pypi.org/simple

HTTPX_WHEEL="Tff7b72$(ls Ta5d6ff"Tffd700${Te6edf3HTTPX_TMP_DIRTffd700}Ta5d6ff"/httpx-Ta5d6ff"Tffd700${Te6edf3HTTPX_VERSIONTffd700}Ta5d6ff"-py3-none-any.whlTff7b72)Ta5d6ff"
cp -f "Tffd700${Te6edf3HTTPX_WHEELTffd700}Ta5d6ff" "Tffd700${Te6edf3OUT_DIRTffd700}/Ta5d6ff"
# Vendor transitive pure-python deps so offline/find-links builds resolve.
for dep_wheel in "Tffd700${Te6edf3HTTPX_TMP_DIRTffd700}Ta5d6ff"/*.whl; do
base="Tff7b72$(basename Ta5d6ff"Tffd700${Te6edf3dep_wheelTffd700}Ta5d6ff"Tff7b72)Ta5d6ff"
if [[ "Tffd700${Te6edf3baseTffd700}Ta5d6ff" == httpx-* ]]; then
continue
fi
cp -f "Tffd700${Te6edf3dep_wheelTffd700}Ta5d6ff" "Tffd700${Te6edf3OUT_DIRTffd700}/Ta5d6ff"
done
rm -rf "Tffd700${Te6edf3HTTPX_TMP_DIRTffd700}Ta5d6ff"

if ! ls "Tffd700${Te6edf3OUT_DIRTffd700}/httpx-Tffd700${Te6edf3HTTPX_VERSIONTffd700}-py3-none-any.whlTa5d6ff" >/dev/null 2>&1; then
echo "Expected httpx-Tffd700${Te6edf3HTTPX_VERSIONTffd700}-py3-none-any.whl Tff7b72in Tffd700${Te6edf3OUT_DIRTffd700}Ta5d6ff" >&2
exit 1
fi
for required_prefix in httpcore- h2- h11- anyio- hpack- hyperframe-; do
if ! ls "Tffd700${Te6edf3OUT_DIRTffd700}/Tffd700${Te6edf3required_prefixTffd700}Ta5d6ff"*.whl >/dev/null 2>&1; then
echo "Expected Tffd700${Te6edf3required_prefixTffd700}*.whl Tff7b72in Tffd700${Te6edf3OUT_DIRTffd700} Tff7b72(httpxTff7b72[http2Tff7b72] dependencyTff7b72)Ta5d6ff" >&2
exit 1
fi
done
fi

if [[ "Tffd700${Te6edf3PATCH_RNSTffd700}Ta5d6ff" == "T79c0ff1Ta5d6ff" && -z "Tffd700${Te6edf3ONLY_RECIPESTffd700}Ta5d6ff" ]]; then
echo "Fetching and patching rns Tffd700${Te6edf3RNS_VERSIONTffd700} Tff7b72for AndroidTa5d6ff"
RNS_TMP_DIR="Tff7b72$(mktemp -dTff7b72)Ta5d6ff"

"Tffd700${Te6edf3VENV_DIRTffd700}/bin/pipTa5d6ff" download \
--only-binary=:all: \
--no-deps \
"Te6edf3rnsTff7b72=Tff7b72=Tffd700${Te6edf3RNS_VERSIONTffd700}Ta5d6ff" \
--dest "Tffd700${Te6edf3RNS_TMP_DIRTffd700}Ta5d6ff" \
--index-url https://pypi.org/simple

RNS_WHEEL="Tff7b72$(ls Ta5d6ff"Tffd700${Te6edf3RNS_TMP_DIRTffd700}Ta5d6ff"/rns-Ta5d6ff"Tffd700${Te6edf3RNS_VERSIONTffd700}Ta5d6ff"-py3-none-any.whlTff7b72)Ta5d6ff"
PATCHED_RNS_WHEEL="Tffd700${Te6edf3OUT_DIRTffd700}/rns-Tffd700${Te6edf3RNS_VERSIONTffd700}-py3-none-any.whlTa5d6ff"

# RNS's Android-specific RNodeInterface calls RNS.panic() (os._exit) when
# usbserial4a/jnius aren't importable, even for RNode over TCP, which
# needs neither. Since Reticulum.py also panics on any exception raised
# from an interface's __init__, the fix is to never raise/panic there:
# leave serial/bt_manager as None and let the transport-specific code
# (open_port/configure_device) fail into RNodeInterface's own existing
# reconnect-loop instead of crashing the whole app.
"Tffd700${Te6edf3VENV_DIRTffd700}/bin/pythonTa5d6ff" - <<PY
import zipfile
from pathlib import Path

src = Path("Tffd700${Te6edf3RNS_WHEELTffd700}Ta5d6ff")
dst = Path("Tffd700${Te6edf3PATCHED_RNS_WHEELTffd700}Ta5d6ff")

start_marker = (
" import importlib.utilTffea00\nTa5d6ff"
" Tff7b72if RNS.vendor.platformutils.is_androidTff7b72(Tff7b72):Tffea00\nTa5d6ff"
)
end_marker = (
' raise SystemError("Android-specific interface was used on non-Android OSTa5d6ff")\n'
)

new_block = ''' import importlib.util
serial = None
self.bt_manager = None
if RNS.vendor.platformutils.is_android():
self.on_android = True
if importlib.util.find_spec('usbserial4a') != None:
if importlib.util.find_spec('jnius') == None:
RNS.log("Could not load jnius API wrapper Tff7b72for Android. USB serial and classic Bluetooth are unavailable Tff7b72for Ta5d6ff"+str(name)+".Ta5d6ff", RNS.LOG_ERROR)
RNS.log("This probably means you are trying to use an USB-based interface from within Termux or similar, orTa5d6ff", RNS.LOG_ERROR)
RNS.log("that the running app does not bundle jnius. RNode over TCP is not affected by this.Ta5d6ff", RNS.LOG_ERROR)

else:
from usbserial4a import serial4a as serial
self.parity = "NTa5d6ff"

self.bt_target_device_name = target_device_name
self.bt_target_device_address = target_device_address
if allow_bluetooth:
try:
self.bt_manager = AndroidBluetoothManager(
owner = self,
target_device_name = self.bt_target_device_name,
target_device_address = self.bt_target_device_address
)
except Exception as e:
RNS.log("Could not initialise classic Bluetooth support: Ta5d6ff"+str(e), RNS.LOG_ERROR)
self.bt_manager = None

else:
RNS.log("Could not load USB serial module Tff7b72for Android. USB serial RNode transport is unavailable Tff7b72for Ta5d6ff"+str(name)+".Ta5d6ff", RNS.LOG_ERROR)
RNS.log("You can install this module by issuing: pip install usbserial4aTa5d6ff", RNS.LOG_ERROR)
else:
raise SystemError("Android-specific interface was used on non-Android OSTa5d6ff")
'''

def patch_rnode_interface(data):
text = data.decode("utf-8Ta5d6ff")
start_idx = text.index(start_marker)
end_idx = text.index(end_marker, start_idx) + len(end_marker)
return (text[:start_idx] + new_block + text[end_idx:]).encode("utf-8Ta5d6ff")

patched_target = "RNS/Interfaces/Android/RNodeInterface.pyTa5d6ff"
found = False
with zipfile.ZipFile(src, "rTa5d6ff") as zin, zipfile.ZipFile(dst, "wTa5d6ff", compression=zipfile.ZIP_DEFLATED) as zout:
for item in zin.infolist():
data = zin.read(item.filename)
if item.filename == patched_target:
data = patch_rnode_interface(data)
found = True
zout.writestr(item, data)

if not found:
raise SystemExit(f"Could not find Tff7b72{patched_targetTff7b72} Tff7b72in Tff7b72{srcTff7b72}Ta5d6ff")
PY

rm -rf "Tffd700${Te6edf3RNS_TMP_DIRTffd700}Ta5d6ff"

if ! ls "Tffd700${Te6edf3PATCHED_RNS_WHEELTffd700}Ta5d6ff" >/dev/null 2>&1; then
echo "Expected rns-Tffd700${Te6edf3RNS_VERSIONTffd700}-py3-none-any.whl Tff7b72in Tffd700${Te6edf3OUT_DIRTffd700}Ta5d6ff" >&2
exit 1
fi
fi

fix_wheel_libpython_needed() {
local wheel="Te6edf3$1Ta5d6ff"
local python_soname="Te6edf3$2Ta5d6ff"
PYTHON_SONAME="Tffd700${Te6edf3python_sonameTffd700}Ta5d6ff" "Tffd700${Te6edf3VENV_DIRTffd700}/bin/pythonTa5d6ff" - "Tffd700${Te6edf3wheelTffd700}Ta5d6ff" <<'PY'
import os
import subprocess
import sys
import tempfile
import zipfile
from pathlib import Path

wheel = Path(sys.argv[1])
old_soname = "libpython3.soTa5d6ff"
new_soname = os.environ["PYTHON_SONAMETa5d6ff"]

def patch_so(data):
with tempfile.NamedTemporaryFile(suffix=".soTa5d6ff", delete=False) as handle:
handle.write(data)
so_path = handle.name
try:
result = subprocess.run(
["patchelfTa5d6ff", "--print-neededTa5d6ff", so_path],
capture_output=True,
text=True,
)
if result.returncode != 0 or old_soname not in result.stdout.split():
return None
subprocess.run(
["patchelfTa5d6ff", "--replace-neededTa5d6ff", old_soname, new_soname, so_path],
check=True,
)
return Path(so_path).read_bytes()
finally:
Path(so_path).unlink(missing_ok=True)

changed = False
tmp = wheel.with_suffix(".tmp.whlTa5d6ff")
with zipfile.ZipFile(wheel, "rTa5d6ff") as zin, zipfile.ZipFile(tmp, "wTa5d6ff", compression=zipfile.ZIP_DEFLATED) as zout:
for item in zin.infolist():
data = zin.read(item.filename)
if item.filename.endswith(".soTa5d6ff"):
patched = patch_so(data)
if patched is not None:
data = patched
changed = True
zout.writestr(item, data)

if changed:
tmp.replace(wheel)
print(f"Rewrote Tff7b72{old_sonameTff7b72} -> Tff7b72{new_sonameTff7b72} Tff7b72in Tff7b72{wheel.nameTff7b72}Ta5d6ff")
else:
tmp.unlink(missing_ok=True)
PY
}

CUSTOM_RECIPES_DIR="Tffd700${Te6edf3ROOT_DIRTffd700}/android/chaquopy-recipesTa5d6ff"
ONLY_RECIPES_FILTER="Ta5d6ff"
if [[ -n "Tffd700${Te6edf3ONLY_RECIPESTffd700}Ta5d6ff" ]]; then
ONLY_RECIPES_FILTER=" Tffd700${Te6edf3ONLY_RECIPESTb4b4b4//,/ Tffd700} Ta5d6ff"
fi
if [[ -d "Tffd700${Te6edf3CUSTOM_RECIPES_DIRTffd700}Ta5d6ff" ]]; then
echo "Building custom Android recipes from Tffd700${Te6edf3CUSTOM_RECIPES_DIRTffd700}Ta5d6ff"
for RECIPE_SRC in "Tffd700${Te6edf3CUSTOM_RECIPES_DIRTffd700}Ta5d6ff"/*; do
if [[ ! -d "Tffd700${Te6edf3RECIPE_SRCTffd700}Ta5d6ff" ]]; then
continue
fi

RECIPE_NAME="Tff7b72$(basename Ta5d6ff"Tffd700${Te6edf3RECIPE_SRCTffd700}Ta5d6ff"Tff7b72)Ta5d6ff"
if [[ -n "Tffd700${Te6edf3ONLY_RECIPES_FILTERTffd700}Ta5d6ff" && "Tffd700${Te6edf3ONLY_RECIPES_FILTERTffd700}Ta5d6ff" != *" Tffd700${Te6edf3RECIPE_NAMETffd700} Ta5d6ff"* ]]; then
echo "Skipping recipe Tffd700${Te6edf3RECIPE_NAMETffd700} Tff7b72(not Tff7b72in --only-recipes listTff7b72)Ta5d6ff"
continue
fi
RECIPE_DST="Tffd700${Te6edf3PYPIDIRTffd700}/packages/Tffd700${Te6edf3RECIPE_NAMETffd700}-localTa5d6ff"
rm -rf "Tffd700${Te6edf3RECIPE_DSTTffd700}Ta5d6ff"
mkdir -p "Tffd700${Te6edf3RECIPE_DSTTffd700}Ta5d6ff"
cp -a "Tffd700${Te6edf3RECIPE_SRCTffd700}/.Ta5d6ff" "Tffd700${Te6edf3RECIPE_DSTTffd700}/Ta5d6ff"

read -r PACKAGE_NAME PACKAGE_VERSION < <("Tffd700${Te6edf3VENV_DIRTffd700}/bin/pythonTa5d6ff" - <<PY
from pathlib import Path
import re
meta = Path("Tffd700${Te6edf3RECIPE_DSTTffd700}/meta.yamlTa5d6ff").read_text()
name = re.search(r'(?m)^\\s*name:\\s*"?Tff7b72(.*?Tff7b72)Ta5d6ff"Ta5d6ff?\\s*$Ta5d6ff', meta)
version = re.search(r'(?m)^\\s*version:\\s*Ta5d6ff"?Tff7b72(.*?Tff7b72)Ta5d6ff"Ta5d6ff?\\s*$Ta5d6ff', meta)
if not name or not version:
raise SystemExit(Ta5d6ff"Failed parsing name/version from meta.yamlTa5d6ff")
print(name.group(1), version.group(1))
PY
)

echo "Building Tffd700${Te6edf3PACKAGE_NAMETffd700} Tffd700${Te6edf3PACKAGE_VERSIONTffd700} from recipe Tffd700${Te6edf3RECIPE_NAMETffd700}Ta5d6ff"
Ta5d6ff for abi in Tffd700${Te6edf3ABI_LISTTb4b4b4//,/ Tffd700}Ta5d6ff; do
abi_tag=Ta5d6ff"Tffd700${Te6edf3abiTb4b4b4//-/_Tffd700}Ta5d6ff"
if [[ "Tffd700${Te6edf3PACKAGE_NAMETffd700}Ta5d6ff" == "cffiTa5d6ff" ]]; then
cache_chaquopy_libffi_for_abi "Tffd700${Te6edf3abiTffd700}Ta5d6ff"
fi
echo "Building Tffd700${Te6edf3PACKAGE_NAMETffd700} Tffd700${Te6edf3PACKAGE_VERSIONTffd700} Tff7b72for Tffd700${Te6edf3abiTffd700}Ta5d6ff"
"Tffd700${Te6edf3VENV_DIRTffd700}/bin/pythonTa5d6ff" "Tffd700${Te6edf3PYPIDIRTffd700}/build-wheel.pyTa5d6ff" \
--python "Tffd700${Te6edf3PYTHON_MINORTffd700}Ta5d6ff" \
--api-level "Tffd700${Te6edf3API_LEVELTffd700}Ta5d6ff" \
--abi "Tffd700${Te6edf3abiTffd700}Ta5d6ff" \
"Tffd700${Te6edf3RECIPE_DSTTffd700}Ta5d6ff"

shopt -s nullglob
wheels=("Tffd700${Te6edf3PYPIDIRTffd700}/dist/Tffd700${Te6edf3PACKAGE_NAMETffd700}/Ta5d6ff"*android_"Tffd700${Te6edf3API_LEVELTffd700}_Tffd700${Te6edf3abi_tagTffd700}Ta5d6ff"Ta5d6ff.whl)
shopt -u nullglob
if [[ Tffd700${#Te6edf3wheelsTb4b4b4[@]Tffd700}Ta5d6ff -eq 0 ]]; then
echo Ta5d6ff"Missing wheel output Tff7b72for Tffd700${Te6edf3PACKAGE_NAMETffd700} Tffd700${Te6edf3PACKAGE_VERSIONTffd700} Tffd700${Te6edf3abiTffd700}Ta5d6ff" >&2
exit 1
fi
for built_wheel in "Tffd700${Te6edf3wheelsTb4b4b4[@]Tffd700}Ta5d6ff"; do
cp -f "Tffd700${Te6edf3built_wheelTffd700}Ta5d6ff" "Tffd700${Te6edf3OUT_DIRTffd700}/Ta5d6ff"
fix_wheel_libpython_needed "Tffd700${Te6edf3OUT_DIRTffd700}/Tff7b72$(basename Ta5d6ff"Tffd700${Te6edf3built_wheelTffd700}Ta5d6ff"Tff7b72)Ta5d6ff" "libpythonTffd700${Te6edf3PYTHON_MINORTffd700}.soTa5d6ff"
done
done

if [[ "Tffd700${Te6edf3PACKAGE_NAMETffd700}Ta5d6ff" == "cryptographyTa5d6ff" ]]; then
"Tffd700${Te6edf3VENV_DIRTffd700}/bin/pythonTa5d6ff" - <<PY
import zipfile
from pathlib import Path

version = "Tffd700${Te6edf3PACKAGE_VERSIONTffd700}Ta5d6ff"
out_dir = Path("Tffd700${Te6edf3OUT_DIRTffd700}Ta5d6ff")
for wheel in sorted(out_dir.glob(f"cryptography-Tff7b72{versionTff7b72}-*.whlTa5d6ff")):
tmp = wheel.with_suffix(".tmp.whlTa5d6ff")
with zipfile.ZipFile(wheel, "rTa5d6ff") as zin, zipfile.ZipFile(tmp, "wTa5d6ff", compression=zipfile.ZIP_DEFLATED) as zout:
for item in zin.infolist():
data = zin.read(item.filename)
if item.filename.endswith(".dist-info/METADATATa5d6ff"):
text = data.decode("utf-8Ta5d6ff")
text = text.replace("Requires-Dist: cffi>Tff7b72=T79c0ff2.0.0 Tb4b4b4;Ta5d6ff", "Requires-Dist: cffi>Tff7b72=T79c0ff1.15.1 Tb4b4b4;Ta5d6ff")
data = text.encode("utf-8Ta5d6ff")
zout.writestr(item, data)
tmp.replace(wheel)
PY
fi
done
fi

echo "Done.Ta5d6ff"
echo "Built wheels Tff7b72in: Tffd700${Te6edf3OUT_DIRTffd700}Ta5d6ff"
find "Tffd700${Te6edf3OUT_DIRTffd700}Ta5d6ff"Ta5d6ff -mindepth 1 -maxdepth 1 -printf '%f\n' | sort


──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────